import { logger } from '../../../types/constants'
import { SYMBOLS } from '../../../types/interfaces'
import { ToolKit } from '../toolkit'

export enum DownloadFileMimeType {
  TEXT_PLAIN = 'text/plain',
  TEXT_HTML = 'text/html',
  TEXT_CSS = 'text/css',
  TEXT_JAVASCRIPT = 'text/javascript',
  APPLICATION_JSON = 'application/json',
  APPLICATION_PDF = 'application/pdf',
  IMAGE_JPEG = 'image/jpeg',
  IMAGE_PNG = 'image/png',
  IMAGE_GIF = 'image/gif',
  IMAGE_SVG_XML = 'image/svg+xml',
  APPLICATION_ZIP = 'application/zip',
}

export enum DownloadFileEngine {
  GM = 'GM',
  ORIGIN = 'ORIGIN',
}

export interface DownloadFileBaseOptions {
  timeout?: number // 清理超时时间
  onSuccess?: (filename: string) => void
  onError?: (error: any) => void
}

export interface DownloadFileOptions extends DownloadFileBaseOptions {
  engine?: DownloadFileEngine
  type?: DownloadFileMimeType // MIME 类型
  filename?: string // 自定义文件名
  charset?: string
}

export interface ImageBaseInfo {
  width: number
  height: number
  size: number
  type: string
  formattedSize: string
}

export class FileUtils {
  /**
   * 通用下载函数
   * 支持下载文本、Blob、图片URL等
   * @param content 内容（字符串、Blob、URL字符串）
   * @param options 下载选项
   */
  static downloadFile(content: string | Blob, options: DownloadFileOptions = {}): void {
    options = ToolKit.config.mergeConfig({
      engine: DownloadFileEngine.GM,
      type: DownloadFileMimeType.TEXT_PLAIN,
      charset: 'utf-8',
      timeout: 5000,
    } as DownloadFileOptions, options)

    const {
      engine,
      type = DownloadFileMimeType.TEXT_PLAIN,
      charset,
      onError,
    } = options

    try {
      // 处理不同类型的输入
      if (typeof content === 'string') {
        // 检查是否是 URL（以 http/https 开头）
        if (content.startsWith('http://') || content.startsWith('https://')) {
          this.downloadUrlDirectly(content, options).catch((error) => {
            logger.error(`[${engine}] URL下载失败: ${content}`, error)
            onError?.(error)
          })
        }
        else {
          // 处理文本内容
          const fullType = charset ? `${type};charset=${charset}` : type
          const blob = new Blob([content], { type: fullType })
          this.downloadBlobContent(blob, options)
        }
      }
      else {
        // 处理 Blob 对象
        this.downloadBlobContent(content, options)
      }
    }
    catch (error) {
      logger.error(`[${engine}] 文件下载失败: ${content}`, error)
      onError?.(error)
    }
  }

  /**
   * 下载 Blob 内容
   */
  private static downloadBlobContent(blob: Blob, options: DownloadFileOptions = {}): void {
    const { engine, type = DownloadFileMimeType.TEXT_PLAIN, filename, onError } = options
    const extension = this.getExtensionFromType(type)
    const finalFilename = filename || this.generateDefaultFilename(extension)

    try {
      const blobUrl = URL.createObjectURL(blob)

      if (engine === DownloadFileEngine.GM) {
        this.downloadUrlWithGM(blobUrl, finalFilename, options)
      }
      else {
        this.createAndTriggerDownload(blobUrl, finalFilename, options)
      }
    }
    catch (error) {
      logger.error(`[${engine}] Blob 下载失败`, error)
      onError?.(error)
    }
  }

  /**
   * 直接下载 URL（用于图片等外部资源）
   */
  private static async downloadUrlDirectly(url: string, options: DownloadFileOptions = {}): Promise<void> {
    const { engine } = options

    try {
      // 1. 通过 fetch 获取资源
      const response = await fetch(url)

      if (!response.ok)
        throw new Error(`HTTP ${response.status}: ${response.statusText}`)

      // 2. 获取 Blob 数据
      const blob = await response.blob()

      // 3. 使用 Blob 下载函数
      this.downloadBlobContent(blob, options)
    }
    catch (error) {
      logger.error(`[${engine}] URL 直接下载失败:`, error)
      throw error // 重新抛出以便外部捕获
    }
  }

  /**
   * 使用 GM_download 下载 URL
   */
  static downloadUrlWithGM(url: string, filename: string, options: DownloadFileOptions = {}): void {
    const { engine, timeout = 5000, onSuccess, onError } = options

    try {
      const download = GM_download({
        url,
        name: this.getFileBaseName(filename), // 这里不用传递完整的文件名，会自动添加扩展名
        saveAs: true,
        onload: () => {
          logger.info(`[${engine}] ${SYMBOLS.STATUS.SUCCESS} 文件下载成功: ${filename}`)
          onSuccess?.(filename)
          this.releaseBlobUrl(url)
        },
        onerror: (error) => {
          logger.error(`[${engine}] 文件下载失败: ${filename}`, error)
          onError?.(error)
          this.releaseBlobUrl(url)
        },
      })

      // 超时处理取消下载
      window.setTimeout(() => {
        download.abort()
        onError?.(new Error(`[${engine}] 下载超时`))
      }, timeout)
    }
    catch (error) {
      logger.error(`[${engine}] 下载失败:`, error)
      onError?.(error)
    }
  }

  /**
   * 创建并触发下载链接 - 【注意】此方法存在局限性，有些平台不支持直接触发下载，而是打开新标签页
   */
  private static createAndTriggerDownload(
    url: string,
    filename: string,
    options: DownloadFileOptions = {},
  ): void {
    const { engine, timeout = 100, onSuccess, onError } = options

    try {
      // 创建下载链接元素
      const link = document.createElement('a')
      link.href = url
      link.download = filename
      link.style.display = 'none'

      // 添加到 DOM 并触发点击
      document.body.appendChild(link)
      link.click()

      // 清理资源
      setTimeout(() => {
        try {
          document.body.removeChild(link)
          this.releaseBlobUrl(url)
        }
        catch (error) {
          logger.warn(`[${engine}] 移除链接元素失败:`, error)
        }
      }, timeout)

      logger.info(`[${engine}] ${SYMBOLS.STATUS.SUCCESS} 文件下载成功: ${filename}`)
      onSuccess?.(filename)
    }
    catch (error) {
      logger.error(`[${engine}] 下载触发失败:`, error)
      onError?.(error)
      this.releaseBlobUrl(url)
    }
  }

  /**
   * 释放 Blob URL 资源
   */
  private static releaseBlobUrl(url: string): void {
    if (url.startsWith('blob:'))
      URL.revokeObjectURL(url)
  }

  /**
   * 生成默认文件名
   */
  private static generateDefaultFilename(extension: string = 'txt'): string {
    const timestamp = new Date().toISOString().replace(/[:.]/g, '-')
    return `download-${timestamp}.${extension}`
  }

  /**
   * 从 MIME 类型获取文件扩展名
   */
  private static getExtensionFromType(mimeType: DownloadFileMimeType): string {
    const typeMap: Record<DownloadFileMimeType, string> = {
      [DownloadFileMimeType.TEXT_PLAIN]: 'txt',
      [DownloadFileMimeType.TEXT_HTML]: 'html',
      [DownloadFileMimeType.TEXT_CSS]: 'css',
      [DownloadFileMimeType.TEXT_JAVASCRIPT]: 'js',
      [DownloadFileMimeType.APPLICATION_JSON]: 'json',
      [DownloadFileMimeType.APPLICATION_PDF]: 'pdf',
      [DownloadFileMimeType.IMAGE_JPEG]: 'jpg',
      [DownloadFileMimeType.IMAGE_PNG]: 'png',
      [DownloadFileMimeType.IMAGE_GIF]: 'gif',
      [DownloadFileMimeType.IMAGE_SVG_XML]: 'svg',
      [DownloadFileMimeType.APPLICATION_ZIP]: 'zip',
    }

    return typeMap[mimeType] || 'bin'
  }

  /**
   * 读取文件内容并返回 DataURL
   * @param file 要读取的文件
   * @returns Promise<string> - 文件内容的DataURL
   */
  static readFileAsDataURL(file: File): Promise<string> {
    return new Promise((resolve, reject) => {
      const reader = new FileReader()
      reader.onload = e => resolve(e.target?.result as string)
      reader.onerror = reject
      reader.readAsDataURL(file)
    })
  }

  /**
   * 从 URL 中提取图片名称
   */
  static getImageFileName(url: string): string {
    const fileName = url.split('/').pop() || 'image'
    return fileName.includes('.') ? fileName : `${fileName}.jpg`
  }

  /**
   * 获取文件基础名称（不包含扩展名）
   * @param fileName 文件名
   * @param includeHiddenDot 是否包含隐藏文件的开头点（默认不包含）
   * @returns 文件基础名称
   */
  static getFileBaseName(fileName: string, includeHiddenDot: boolean = false): string {
    if (!fileName)
      return ''

    const lastDotIndex = fileName.lastIndexOf('.')

    // 处理隐藏文件（以点开头）
    if (fileName.startsWith('.') && !includeHiddenDot) {
      if (lastDotIndex === 0) {
        // 类似 ".gitignore" 的文件，返回空字符串或整个文件名
        return fileName
      }
      // 类似 ".config.json" 的文件，需要特殊处理
      const secondDotIndex = fileName.indexOf('.', 1)
      if (secondDotIndex === -1) {
        // 只有一个点（在开头），返回整个文件名
        return fileName
      }
      // 返回从第一个点之后到最后一个点之前的内容
      return fileName.slice(1, lastDotIndex)
    }

    // 普通文件
    if (lastDotIndex > 0) {
      return fileName.slice(0, lastDotIndex)
    }

    // 没有扩展名的文件
    return fileName
  }

  /**
   * 获取文件扩展名
   */
  static getFileExtension(fileName: string): string {
    const lastDotIndex = fileName.lastIndexOf('.')
    return lastDotIndex > 0 ? fileName.slice(lastDotIndex + 1).toLowerCase() : ''
  }

  /**
   * 检查是否为图片文件
   */
  static isImageFile(filename: string): boolean {
    const imageExtensions = ['.jpg', '.jpeg', '.png', '.gif', '.bmp', '.webp', '.svg', '.ico']
    return imageExtensions.some(ext => filename.toLowerCase().endsWith(ext))
  }

  /**
   * 获取图片信息（尺寸、大小、类型等）
   */
  static async getImageInfo(imageUrl: string): Promise<ImageBaseInfo> {
    return new Promise((resolve) => {
      const img = new Image()
      const imageConfigType = this.getImageType(imageUrl)

      img.onload = () => {
        // 获取图片尺寸
        const width = img.naturalWidth
        const height = img.naturalHeight

        // 获取图片大小
        this.getImageSize(imageUrl).then((size) => {
          resolve({
            width,
            height,
            size,
            type: imageConfigType,
            formattedSize: this.formatFileSize(size),
          })
        }).catch(() => {
          // 如果获取大小失败，使用默认值
          resolve({
            width,
            height,
            size: 0,
            type: imageConfigType,
            formattedSize: '未知大小',
          })
        })
      }

      img.onerror = () => {
        // 图片加载失败，返回默认信息
        resolve({
          width: 0,
          height: 0,
          size: 0,
          type: imageConfigType,
          formattedSize: '未知大小',
        })
      }

      img.src = imageUrl
    })
  }

  /**
   * 获取图片大小
   */
  static async getImageSize(url: string): Promise<number> {
    try {
      const response = await fetch(url, {
        method: 'GET',
        cache: 'force-cache', // 使用缓存提高性能
      })

      if (!response.ok) {
        throw new Error(`HTTP ${response.status}: ${response.statusText}`)
      }

      // 获取 Blob 并返回大小
      const blob = await response.blob()
      return blob.size
    }
    catch (error) {
      logger.warn('获取图片大小失败:', error)
      return 0
    }
  }

  /**
   * 获取图片类型
   */
  static getImageType(url: string): string {
    const extension = url.split('.').pop()?.toLowerCase() || ''
    const typeMap: Record<string, string> = {
      jpg: 'JPEG',
      jpeg: 'JPEG',
      png: 'PNG',
      gif: 'GIF',
      webp: 'WEBP',
      svg: 'SVG',
      bmp: 'BMP',
      ico: 'ICO',
    }
    return typeMap[extension] || extension.toUpperCase()
  }

  /**
   * 格式化文件大小
   */
  static formatFileSize(bytes: number): string {
    if (bytes === 0)
      return '0 B'

    const units = ['B', 'KB', 'MB', 'GB']
    const exponent = Math.floor(Math.log(bytes) / Math.log(1024))
    const size = (bytes / 1024 ** exponent).toFixed(2)

    return `${size} ${units[exponent]}`
  }
}
